/**
 * 测试select函数使用,维护一个数组来优化select调用；
 * @author: sundx
 * @date: 2020-05-31 13:49:46
 * @Last Modified by: sundx
 * @Last Modified time: 2020-05-31 22:43:18
 */

#include "wrap.h"
#include <cstring>
#include <iostream>

int main() {
  int maxFd = -1;
  int maxIndex = -1, index = 0;
  struct sockaddr_in clientAddr;
  socklen_t addrLen = sizeof(clientAddr);
  //记录通信Fd到此数组 最多1023个，加上监听Fd一共1024

  int allSetIndex[FD_SETSIZE - 1];
  memset(allSetIndex, -1, 1023 * 4); //设置为 -1；
  int listenFd = sdx::serverSocketBindListen();
  maxFd = listenFd;         // 设置最大文件描述符；
  fd_set readFdSet, allSet; // 设置读集合，以及所有文件描述符所在集合
  FD_ZERO(&allSet);
  FD_SET(listenFd, &allSet); //将监听Fd加入所有文件描述符集合；
  while (1) {                //循环 select
    readFdSet = allSet; //初始化读集合，每次触发信事件之后都要初始化
    // 设置阻塞状态的select；
    int activeFd = select(maxFd + 1, &readFdSet, nullptr, nullptr, NULL);
    if (activeFd == -1) {
      sdx::printError("select error...");
    }

    // 判断是否为监听Fd的事件，是否来了新链接
    if (FD_ISSET(listenFd, &readFdSet)) {
      int connFd = sdx::Accept(listenFd, (sockaddr *)&clientAddr, &addrLen);
      for (index = 0; index < FD_SETSIZE - 1; ++index) { //通信Fd最多1023个
        if (allSetIndex[index] < 0) { //找到第一个为-1的位子；
          allSetIndex[index] = connFd;
          break;
        }
      }
      if (index == FD_SETSIZE - 1) { //通信fd到达了1023个
        std::cout << "too many clients..." << std::endl;
        exit(EXIT_FAILURE);
      }
      if (maxFd < connFd)
        maxFd = connFd;
      if (index > maxIndex)
        maxIndex = index;
      FD_SET(connFd, &allSet);
      if (--activeFd == 0) //判断是否只来了一个读事件，而且是监听Fd的；
        continue;
    }
    // 从监听Fd之后一个fd开始，逐个检查是否是通信Fd的读事件；
    for (int i = 0; i <= maxIndex; ++i) {
      if (allSetIndex[i] < 0)
        continue;
      if (FD_ISSET(allSetIndex[i], &readFdSet)) {
        char inbuff[MESSAGE_SIZE];   //读缓冲区
        bzero(inbuff, MESSAGE_SIZE); //初始化缓冲区
        int recvBytes = recv(allSetIndex[i], inbuff, MESSAGE_SIZE, 0);
        if (recvBytes == 0) { //判断是否是客户端关闭
          std::cout << " close the connect..." << std::endl;
          close(allSetIndex[i]);
          FD_CLR(allSetIndex[i], &allSet); //从allset中去除当前已经关闭的i；
          allSetIndex[i] = -1;
        } else if (recvBytes > 0) {
          std::cout << "receive " << recvBytes << " bytes message"
                    << "and the message is " << inbuff << std::endl;

          for (int j = 0; j < recvBytes; ++j) { // 小写转大写
            inbuff[j] = toupper(inbuff[j]);
          }

          int ret = send(allSetIndex[i], inbuff, recvBytes, 0); //发送
          if (ret == -1) {
            sdx::printError("recv failed");
          }
          //判断当前是否为最后一个激活的Fd，如果是则跳出循坏；
          if (--activeFd == 0)
            break;
        } else { //判断是否出错；
          if ((errno == ECONNABORTED) || (errno == EINTR)) {
            continue;
          } else {
            sdx::printError("recv error...");
          }
        }
      }
    }
  }
  return 0;
}